#include "mainwindowimpl.h"
//#include "variablelist.h"
#include "variable.h"
#include <math.h>
#include <QStandardItemModel>
#include "storevariable.h"


//--- Debug ---
#include <iostream>
#include<QString>
using namespace std;
//
MainWindowImpl::MainWindowImpl( QWidget * parent, Qt::WFlags f) 
	: QMainWindow(parent, f)
{
	setupUi(this);
	
	// --- Fixation des tailles des éléments ---
	//undoButton->setFixedSize(45,28);
	//redoButton->setFixedSize(45,28);
	
	// --- Creation du Stack Dock et de ses entrees menu ---
	stackDock=new QDockWidget("Stack",this);
	stackDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
	Stack=new VariableStack(stackDock);
	addDockWidget(Qt::RightDockWidgetArea,stackDock);
	stackDock->setWidget(Stack);
	menuView->addAction(stackDock->toggleViewAction());
	
	// --- Variable Res ---
	Variable *V;
	V=new Variable("Res",0,"");
	List->put(V);
	delete V;
	
	// --- Connexion des signaux ---
	connect(undoButton,SIGNAL(clicked()),&UDS,SLOT(undo()));
	connect(redoButton,SIGNAL(clicked()),&UDS,SLOT(redo()));
	connect(Command,SIGNAL(customReturnPressed()),this,SLOT(SlotExecuteCommand()));
}

void MainWindowImpl::ErrorFromCode(int code,QString S){
	QString Errormessage;
	switch(code){
		case 1 : Errormessage="Empty Command"; break;
		case 2 : Errormessage="Too much = sign"; break;
		case 3 : Errormessage="Problem with parenthesis"; break;
		case 4 : Errormessage="Error computing expression : "; Errormessage.append(S); break;
		case 5 : Errormessage="Unimplemented Operation : "; Errormessage.append(S); break;
	}
	Display->setText(Errormessage);
}

void MainWindowImpl::SlotExecuteCommand(){
	QString CommandText=Command->displayText();
	
	ExecuteCommand(CommandText);
}

void MainWindowImpl::ExecuteCommand(QString &CommandText){
	CommandText.remove(QChar(' '),Qt::CaseInsensitive);
	QStringList list = CommandText.split("=", QString::SkipEmptyParts);
	
	bool OK=true;
	QString Name;
	double Data;
	QString Expression;
	
	if(list.size()==0){
		OK=false;
		ErrorFromCode(1);
	}
	else if(list.size()==1){
		Name="Res";
		Expression=list.at(0);
	}
	else if(list.size()==2){
		Name=list.at(0);
		Expression=list.at(1);
	}
	else{
		OK=false;
		ErrorFromCode(2);
	}
	
	if(OK){
		cout<<Expression.toStdString()<<endl;
		Data=ComputeExpression(Expression,OK);
	}
	if(OK){
		Variable *V;
		V=new Variable(Name,Data,Expression);
		storeVariable *Com;
		Com = new storeVariable(V,List,Stack);
		UDS.push(Com);
		Display->setText("");
		Command->clear();
		delete V;
	}
}

double MainWindowImpl::ComputeExpression(QString Expression,bool &OK){
	cout<<"Computing Expression : "<<Expression.toStdString()<<endl;
	QList<QString> OperandeStack; OperandeStack.clear();
	QList<QString> OperationStack; OperationStack.clear();
	double result=0;
	OK=true;
	if(Expression.startsWith("$")) result=ComputeExpressionRPN(Expression,OK);
	else{
		/* On parcours toute l'expression carcatère par caractère.
		-> Si on rencontre une parenthèse ouvrante, on ajoute le contenu de la parenthèse
			sur la pile des opérandes
		-> Si on rencontre un signe d'opération, on l'ajoute sur la pile des opération
		-> Les Nombre et Variables sont ajoutés sur la pile des opérations. */
		cout<<"It's not RPN"<<endl;
		int count=0;
		QString S;
		S.clear();
		QString SS;
		SS.clear();
		cout<<"Expr size is "<<Expression.size()<<endl;
		while(count<Expression.size()&&OK){
			cout<<"Accessing char nbr"<<count<<"in the expression"<<endl;
			S=Expression.right(Expression.size()-count);
			if(S.startsWith("(")){
				// On cherche la parenthèse équivalente
				cout<<"Parenthesis found"<<endl;
				int cor=1;
				int parlvl=1;
				while(parlvl!=0&&OK){
					if(cor>S.size()){ErrorFromCode(3); OK=false;}
					else if(QString(S[cor])=="(") parlvl++;
					else if(QString(S[cor])==")") parlvl--;
					cor++;
				}
				SS=S.mid(1,cor-2);
				cout<<"Inside parenthesis : "<<SS.toStdString()<<endl;
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				count=count+cor;
			}
			else if(S.startsWith(")")){
				ErrorFromCode(3); 
				OK=false;
			}
			else if(S.startsWith("+")){
				cout<<"Addition found"<<endl;
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperationStack.append("+");
				count++;
			}
			else if(S.startsWith("-")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				if(count==0) OperandeStack.append("0");
				SS.clear();
				OperationStack.append("-");
				count++;
			}
			else if(S.startsWith("*")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperationStack.append("*");
				count++;
			}
			else if(S.startsWith("/")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperationStack.append("/");
				count++;
			}
			else if(S.startsWith("^")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperationStack.append("^");
				count++;
			}
			else if(S.startsWith("sqrt(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("sqrt");
				count=count+4;
			}
			else if(S.startsWith("sin(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("sin");
				count=count+3;
			}
			else if(S.startsWith("cos(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("cos");
				count=count+3;
			}
			else if(S.startsWith("tan(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("tan");
				count=count+3;
			}
			else if(S.startsWith("asin(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("asin");
				count=count+4;
			}
			else if(S.startsWith("acos(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("acos");
				count=count+4;
			}
			else if(S.startsWith("atan(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("tan");
				count=count+4;
			}
			else if(S.startsWith("log(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("log");
				count=count+3;
			}
			else if(S.startsWith("ln(")){
				if(!SS.isEmpty()) OperandeStack.append(SS);
				SS.clear();
				OperandeStack.append("0");
				OperationStack.append("ln");
				count=count+2;
			}
			else{
				cout<<"Simple var/number"<<endl;
				SS.append(S.at(0));
				count++;
			}
		}
		if(OK){
			cout<<"Gonna solve it"<<endl;
			if(!SS.isEmpty())OperandeStack.append(SS);
			result=SolveStack(OperandeStack,OperationStack,OK);
		}
	}
	cout<<"result is"<<result<<endl;
	return result;
}

double MainWindowImpl::ComputeExpressionRPN(QString Expression,bool &OK){
	return 0;
}

bool MainWindowImpl::isExpression(QString E){
	bool Expr=false;
	QList<QString> Operators;
	Operators<<"("<<"+"<<"-"<<"*"<<"/"<<"^"<<"sqrt"<<"sin("<<"cos("<<"tan("<<"asin("<<"acos("<<"atan("<<"log("<<"ln(";
	int count=0;
	while(count<Operators.size()&&Expr==false){
	Expr=E.contains(Operators[count]);
	count++;
	}
	return Expr;
}

double MainWindowImpl::SolveStack(QList<QString> OperandeStack,QList<QString> OperationStack,bool &OK){
	cout<<"Taille operande : "<<OperandeStack.size()<<" taille operation "<<OperationStack.size()<<endl;
	double res=0;
	QString Operation;
	QString OperandeA;
	QString OperandeB;
	bool AOK=true;
	bool BOK=true;
	double OpA;
	double OpB;

	while(!OperationStack.isEmpty()||!OperandeStack.isEmpty()){
		if(OperandeStack.size()==1&&OperationStack.isEmpty()){
			cout<<"No operation"<<endl;
			OperandeA=OperandeStack.takeFirst();
		
			OpA=OperandeA.toDouble(&AOK);
			if(!AOK) OpA=List->getValFromName(OperandeA,AOK);
			if(!AOK && isExpression(OperandeA)) OpA=ComputeExpression(OperandeA,AOK);
			if(!AOK) ErrorFromCode(4,OperandeA);
			cout<<"OPA = "<<OpA<<endl;
			res=OpA;
		}
		else{
			Operation=OperationStack.takeFirst();
			OperandeA=OperandeStack.takeFirst();
			OperandeB=OperandeStack.takeFirst();
			cout<<"Processing operation : "<<Operation.toStdString()<<endl;
			
			OpA=OperandeA.toDouble(&AOK);
			if(!AOK) OpA=List->getValFromName(OperandeA,AOK);
			if(!AOK && isExpression(OperandeA)) OpA=ComputeExpression(OperandeA,AOK);
			if(!AOK) ErrorFromCode(4,OperandeA);
			cout<<"With OpA = "<<OperandeA.toStdString()<<" -> "<<OpA<<endl;
			
			OpB=OperandeB.toDouble(&BOK);
			if(!BOK) OpB=List->getValFromName(OperandeB,BOK);
			if(!BOK && isExpression(OperandeB)) OpB=ComputeExpression(OperandeB,BOK);
			if(!BOK) ErrorFromCode(4,OperandeB);
			cout<<"With OpB = "<<OperandeB.toStdString()<<" -> "<<OpB<<endl;
		
			if(AOK && BOK){
				OK=true;
				if(Operation=="+"){res=OpA+OpB; OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="-"){res=OpA-OpB; OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="*"){res=OpA*OpB; OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="/"){res=OpA/OpB; OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="^"){res=pow(OpA,OpB); OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="sqrt"){res=sqrt(OpB); OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="sin"){res=sin(OpB*3.141692/180); OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="cos"){res=cos(OpB*3.141692/180); OperandeStack.prepend(QString("%1").arg(res));}
				else if(Operation=="tan"){res=tan(OpB*3.141692/180); OperandeStack.prepend(QString("%1").arg(res));}
				else { ErrorFromCode(5,Operation); OK=false;}
			}
			else OK=false;
		}
		//if(AOK && BOK) OK=true;
		//else OK=false;
	}
	return res;
}
//
